#! /usr/bin/env perl
##
## Copyright (C) by Argonne National Laboratory
##     See COPYRIGHT in top-level directory
##

# Set Defaults
$debug = 0;
$debugDir = 0;
$gDebugOther = 0;

$summaryOutput = 0;
$terseOutput = 1;
$rootSrcDir = "";
# Define the known commands, along with known make noise (results of
# echo)
# We include /bin/rm and /bin/mv because some Makefile authors prefer
# to get the full path for these routines
@commands = ( "cc", "pgcc", "gcc", "icc", "acc", 
              "rm", "mv", "cp", "ar", "ranlib", "perl", "for", 
	      "/bin/rm", "/bin/mv", "/bin/cp", "/bin/chmod",
	      "rm", "mv", "cp", "chmod",
	      "if", "make", "gnumake", 
	      "[A-Za-z0-9_\/\.-]*\/mpicc",
	      "[A-Za-z0-9_\/\.-]*\/mpifort",
	      "[A-Za-z0-9_\/\.-]*\/mpicxx",
	      "cleaning", "sleep", "date", "g77", "f77", "f90", "f95", "pgCC",
	      "pgf77", "pgf90", "CC", "g95", "g\\+\\+", "c\\+\\+",
	      "acc\\+\\+", "xlc", "xlf", "xlC", "xlf90", "gfortran", 
	      "ifort", "ifc", "test",
	      "true", "false", "/[A-Za-z0-9_\/\.-]*createshlib", 
	      "/bin/sh\\s+[A-Za-z0-9_\/\.-]*libtool\\s+\(--tag=\\w+\)*\\s+--mode=\\w+",
	      "/bin/bash\\s+[A-Za-z0-9_\/\.-]*libtool\\s+--mode=\\w+",
	      "/bin/sh\\s+[A-Za-z0-9_\/\.-]*libtool\\s+--finish",
	      "/bin/bash\\s+[A-Za-z0-9_\/\.-]*libtool\\s+--finish",
	      "libtool:\\s+compile:",
	      "libtool:\\s+link:",
	      "libtool:\\s+install:",
	      "libtool:\\s+finish:",
	      "[A-Za-z0-9_\/-]*\/icc",
	      "[A-Za-z0-9_\/-]*\/install-sh",
	      "/usr/bin/ar", "mkdir",
              "/bin/mkdir",
              "/.*bin/mkdir",
	      "/usr/ucb/ld",
	      "mpicc", "mpicxx",
	      "compiling ROMIO in", 
	      "Make completed",
	      "echo", "cat",
	      "NVCC", "OCLOC", "CC", "CCLD", "AR", "RANLIB", "LIBTOOL", "MV",
              "MOD", "GEN", "F77", "FC", "CXXLD", "CXX", "FCLD",
	      "CP", # "terse" make versions
	      "\\(?cd",
	      "\\(\\s*cd",
	      "creating\s+lib.*",
	      "cd\\s+\&\&\\s+make",
	      "sed -e",
	      "PATH=\"\\\$PATH.*\\sldconfig\\s",
	      "building profiling interface in directory",
	      "/usr/bin/install",
              "/.*bin/install",
              ".*confdb/install-sh",
	      "Copying Upshot",
	      "Cleaning directory",
	      "[*][*][*][*] Making .*\.\.\.\.",
	      "Making .* in .*",
	      "Making upshot", "\\s*\$",
              "ld: warning: directory not found for option '-L\\S*src/mpl'",
              "ld: warning: directory not found for option '-L\\S*src/openpa/src'",
	      "copying selected object files to avoid basename conflicts...",
);

# OtherNoise is an array of patterns that describe blocks of
# text that we'd like to skip.  The initial use it to handle 
# libtool install output.
# The PG entries are work-arounds for bugs in the PG header files that have
# small incompatibilities with the system header file /usr/include/unistd.h
#
@OtherNoise = ( 'Libraries have been installed in:<SEP>more information, such as the ld\(1\) and ld.so\(8\) manual pages.<SEP>20',
		'^\s*-+\s*$<SEP><SEP>1', 
		'^In directory:<SEP><SEP>1',
		'^creating\s<SEP><SEP>1', 
#		'^PGC-W-0114-More than one type.*/usr/include/unistd.h: 189<SEP>^PGC-W-0143-Useless typedef.*/usr/include/unistd.h: 189<SEP>2',
#		'^PGC-W-0114-More than one type.*/usr/include/unistd.h: 243<SEP>^PGC-W-0143-Useless typedef.*/usr/include/unistd.h: 243<SEP>2',
		'^PGC.*: compilation completed with warnings<SEP><SEP>1',
# This next is a general version to handle all of these mistakes
		'^PGC-W-0114-More than one type specified.*/usr/include<SEP>^PGC-W-0143-Useless typedef declaration \(no declarators present\).*/usr/include<SEP>2',
		'copying python',
		'LOOP WAS VECTORIZED',
		'Using variables ',
		);
# In the past, we also needed
# WARNING 84.*not used for resolving any symbol
# Info: File not optimized


$trimCompileLine = 1;

# Create the variables that keep track of state
# dirstack keeps the directory name that gnumake and similar make
# implementations echo as a directory is entered or exited (this can generate
# a great deal of output noise that can obscure important data.  However, when
# a problem *does* occur, this directory information is valuable.
# dirprinted is a parallel array that indicates whether the corresponding
# directory entry has been printed.
@dirstack = ();
@dirprinted = ();

# Process arguments to select options
foreach $_ (@ARGV) {
    if (/^--?debug/) { $debug = 1; }
    elsif (/^--?showdir/) { $debugDir = 1; }
    elsif (/^--?summary/) { $summaryOutput = 1; }
    elsif (/^--?notrimcompileline/) { $trimCompileLine = 0; }
    elsif (/^-/) {
	print STDERR "Unrecognized argument $_\n";
	print STDERR "clmake [ -debug ] [ -showdir ] [ -summary ]\n";
	exit(1);
    }
    else { last; }
    shift @ARGV;
}
# Read lines.  Categorize lines as commands and output.  This
# script suppresses commands that generate no extra output.
# The approach is to read a line and then read the next line.
# While the line is a command and the next line is not, output the
# line.
#
# We use eof() (see man perlfun) so that we can accept filenames on the
# commandline.
#
$cmdline = "";
T:
while ($line = <>) {
    if (eof()) { next; }

    $line =~ s/\r//;    # remove returns from line (de DOSify)

    # Read the next line, including handling any continuation lines
    while ($line =~ /\\$/) {
	print "Read continuation line (cmd) ... \n" if $debug;
	$line .= <>;
    }

    # Check for noise
    foreach my $pat (@OtherNoise) {
	# maxlines is the maximum number of lines to match
	my ($beginpat,$endpat,$maxlines) = split( '<SEP>', $pat );
	print "Trying to match $beginpat\n" if $gDebugOther;
	if ($line =~ /$beginpat/) {
	    $maxlines --;
	    print "Found match to $beginpat\n" if $gDebugOther;
	    # Found the beginning.  Look for the end (if one requested)
	    if ($endpat =~ /\S/) {
		print "Trying to match endpat $endpat\n" if $gDebugOther;
		while ($line = <>) {
		    if (eof()) { last; }
		    print "Trying to match to $line" if $gDebugOther;
		    if ($line =~ /$endpat/) { last; }
		    if ($maxlines-- <= 0) { 
			print STDOUT "Failed to match $endpat; last line was $line";
			last; 
		    }
		}
	    }
	    next T;
	}
    }
    
    # Is the line a command (including a directory change?)
    # Check first for a directory change
    if ($line =~ /^\s*make.* Entering directory \`([^\']*)\'/) {
	my $dirname = $1;
	$dirstack[$#dirstack+1] = $dirname;
	$dirprinted[$#dirprinted+1] = 0;
	print ">>entering $dirname\n" if $debugDir;
	$cmdline = "";
	next;
    }
    elsif ($line =~ /^\s*make.* Leaving directory \`([^\']*)\'/) {
	# We should check that the directory names match
	my $dirname = $1;
	print ">>leaving $dirname\n" if $debugDir;
	if ($dirname ne $dirstack[$#dirstack]) {
	    print STDERR "Warning: leaving directory $dirname but expected to leave directory " . $dirstack[$#dirstack] . "\n";
	}
	$#dirstack--;
	$#dirprinted--;
	$cmdline = "";
	next;
    }
    else {
	$is_command = 0;
	foreach $cmdname (@commands) {
	    if ($line =~ /^\s*$cmdname\W/) {
		$is_command = 1;
		$cmdline = $line;
		last;
	    }
	}
	if ($is_command) { 
	    if ($summaryOutput) {
		# FIXME: Summarize the command
		if ($terseOutput) {
		    if ($rootSrcDir eq "") {
			if ($line =~ /\s(\/\S+\/src\/)/) {
			    $rootSrcDir = $1;
			    print "rootSrcDir = $rootSrcDir\n";
			}
		    }
		    else {
			$line =~ s/$rootSrcDir//g;
		    }
		    # Compiler options to remove
		    $line =~ s/-I\S+\s+//g;
		    $line =~ s/-W\S+\s+//g;
		    $line =~ s/-D\S+\s+//g;
		}
		print $line;
	    }
	    next; 
	}
    }

    # If we got to this point, the line was not a recognized command or
    # ignorable output.  Output the line, including the command if this 
    # is the first time.  We can then forget the command 
    if ($#dirstack >= 0 && $dirprinted[$#dirstack] == 0) {
	print "In directory: ". $dirstack[$#dirstack] . "\n";
	$dirprinted[$#dirstack] = 1;
    }
    if ($cmdline ne "") { 
	if ($trimCompileLine) {
	    # Simplify the command line before printing
	    $cmdline =~ s/-I\S*\s+//g;
	    $cmdline =~ s/-ansi//g;
	    $cmdline =~ s/-W\S*\s+//g;
	    $cmdline =~ s/-DHAVE_CONFIG_H//g;
	    $cmdline =~ s/-DGCC_WALL//g;
	}
	# We could print a newline here to separate commands
	print $cmdline;
	$cmdline = "";
    }
    print $line;
}

#
# ToDo:
# Handle lines containing just ----, e.g., patterns of the form
# -*
